<?php
/*
 * Copyright 2010 Johannes M. Schmitt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class closureGeneratedepsTask extends sfBaseTask
{
  protected function configure()
  {
    // // add your own arguments here
    // $this->addArguments(array(
    //   new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
    // ));

    $this->addOptions(array(
      new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
      // add your own options here
    ));

    $this->namespace        = 'closure';
    $this->name             = 'generate-deps';
    $this->briefDescription = '';
    $this->detailedDescription = <<<EOF
The [closure:generate-deps|INFO] task compiles the dependencies for the Google Closure Library.
It works with Javascript spread across several plugins regardless of whether their assets are
published by copying (mostly on Windows), or by creating symlinks (mostly Unix).

The generated dependencies file will be placed at %SF_WEB_DIR%/js/compiled/deps.js

Call it with:

  [php symfony closure:generate-deps|INFO]
EOF;
  }

  protected function execute($arguments = array(), $options = array())
  {
  	$baseFile = GoogleClosureUtil::getBaseFile();
    $this->logSection('closure', 'Found base file at '.$baseFile);

    // find the path to the calcdeps.py file
    $files = sfFinder::type('file')
    			->name('calcdeps.py')
    			->follow_link()
    			->in(sfConfig::get('sf_web_dir'));
    if (count($files) === 0)
    	throw new RuntimeException('Could not locate calcdeps.py');
    else if (count($files) > 1)
    	throw new RuntimeException('Multiple calcdeps.py files found.');
    $calcDeps = $files[0];
    $this->logSection('closure', 'Located calcdeps.py at '.$calcDeps);
    
    // generate dependencies
    chdir(dirname($baseFile));
    
    // get all web directories which could contain Javascript files
   	$sfWebDir = sfConfig::get('sf_web_dir');
   	$sfWebDir = str_replace('\\', '/', $sfWebDir);
    $paths = array('-p '.escapeshellarg($sfWebDir));
    foreach ($this->configuration->getPluginSubPaths('/web') as $pluginWebDir)
    {
    	$pluginWebDir = str_replace('\\', '/', $pluginWebDir);
    	$pluginName = substr($pluginWebDir, strrpos($pluginWebDir, '/', -5) + 1, -4);
    	
	    // we only need to add plugin sepcific web directories if the plugin's
	    // assets have not been published, or are published using a symlink
	    // since the calcdeps.py is not following symlinks
    	if (file_exists($sfWebDir.'/'.$pluginName) === false
          || is_link($sfWebDir.'/'.$pluginName) === true)
          $paths[] = '-p '.escapeshellarg($pluginWebDir);
    }
    
    $cmd = sprintf('%s %s %s -o deps', 
    		self::getPythonCli(), 
    		escapeshellarg($calcDeps), 
    		implode(' ', $paths)
    	  );
    
    exec($cmd, $output, $returnVar);
    if ($returnVar !== 0)
    	throw new RuntimeException('An error occured during generation.');
    
    // get the paths relative to the base file
    $normalizedBaseDir = str_replace('\\', '/', $baseFile);
    $c = substr_count($normalizedBaseDir, '/', strpos($normalizedBaseDir, 'web/'));
    $prefix = '';
    while ($c > 0)
    {
    	$prefix .= '../';
    	$c -= 1;
    }
    	
    // fix the prefix so it's relative to base.js
    $targetFile = sfConfig::get('sf_web_dir').'/js/compiled/deps.js';
    $webDir = sfConfig::get('sf_web_dir');
    
    // normalize the output
    $rootDirName = basename(sfConfig::get('sf_root_dir'));
		foreach ($output as $i => $line)
		{
		    $line = str_replace('\\\\', '\\', $line);
		    $line = str_replace('\\', '/', $line);
		    
		    $line = preg_replace_callback('#[^"]+/([^/]+)/web/([^"]+)#i', 
			    function($match) use ($prefix, $rootDirName) {
			    	// this refers to the regular web_dir located directly in the root folder
			    	if ($match[1] == '..' || $match[1] == $rootDirName)
			    		$return = $prefix.$match[2];
			      // this refers to plugin directories
			    	else 
			    		$return = $prefix.$match[1].'/'.$match[2];
			    		
			    	return $return;
			    }, $line);
		    
		    $output[$i] = $line;
		}    
    
		$output = implode("\n", $output);
    file_put_contents($targetFile, $output);
    $this->logSection('file+', $targetFile);
  }
  
	/**
	 * Returns the path to the Python CLI
	 * @return string
	 */
	public static function getPythonCli()
	{
	    $path = getenv('PATH') ? getenv('PATH') : getenv('Path');
	    $suffixes = DIRECTORY_SEPARATOR == '\\' ? (getenv('PATHEXT') ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe', '.bat', '.cmd', '.com')) : array('');
	    foreach (array('python') as $cli)
	    {
	      foreach ($suffixes as $suffix)
	      {
	        foreach (explode(PATH_SEPARATOR, $path) as $dir)
	        {
	          if (is_file($file = $dir.DIRECTORY_SEPARATOR.$cli.$suffix) && is_executable($file))
	          {
	            return $file;
	          }
	        }
	      }
	    }
	
	    throw new RuntimeException('Unable to find Python executable.');
	}
}
